package edu.amm.fanal.ui;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.AnchorPane;
import edu.amm.api.Bounds;
import edu.amm.fanal.api.Function;

/**
 * Контроллер формы для отображения графиков.
 * 
 * @author Иванов Илья
 * @since 2013-05-19
 *
 */
public class GraphicsFormController implements Initializable {
	
	@FXML
	private TabPane tabs;
	@FXML
	private MenuItem exit;
	@FXML
	private MenuItem newSolving;
	
	public void initialize(URL location, ResourceBundle resources) {
		initTabs();
		setHandlers();		
	}
	
	public void showGraphic(GraphicData graphic) {
		if (graphic == null)
			return;
		
		addTab(graphic);
	}
	
	private void initTabs() {
		tabs.getTabs().clear();
	}
	
	private void setHandlers() {
		exit.setOnAction(new EventHandler<ActionEvent>() {
			
			public void handle(ActionEvent event) {
				SolverApplication.exit();
			}
		});
		
		newSolving.setOnAction(new EventHandler<ActionEvent>() {
			
			public void handle(ActionEvent event) {
				SolverApplication.getApplication().showDataInputForm();
			}
		});
	}
	
	private void addTab(GraphicData data) {
		NumberAxis xAxis = new NumberAxis();
		NumberAxis yAxis = new NumberAxis();
		
		LineChart<Number, Number> lineChart = new LineChart<>(xAxis, yAxis);
		
		XYChart.Series<Number, Number> approx = new XYChart.Series<>();
		approx.setName("Приближённое решене");
		XYChart.Series<Number, Number> exact = null;
		if (data.getExactSolution() != null) {
			exact = new XYChart.Series<>();
			exact.setName("Точное решение");
		}
		
		int count = data.getSegmentsCount();
		double a = data.getBounds().getLeftBound();
		double b = data.getBounds().getRightBound();
		double step = (b - a) / count;
		
		for (int i = 0; i <= count; i++) {
			double x = a + i * step;
			
			double y = data.getApproximateSolution().evaluate(x);
			approx.getData().add(new Data<Number, Number>(x, y));
			
			if (data.getExactSolution() != null) {
				y = data.getExactSolution().evaluate(x);
				exact.getData().add(new Data<Number, Number>(x, y));
			}
		}
		
		lineChart.getData().add(approx);
		if (exact != null)
			lineChart.getData().add(exact);
		
		AnchorPane anchor = new AnchorPane();
		anchor.getChildren().add(lineChart);
		AnchorPane.setLeftAnchor(lineChart, 5.0);
		AnchorPane.setTopAnchor(lineChart, 5.0);
		AnchorPane.setRightAnchor(lineChart, 5.0);
		AnchorPane.setBottomAnchor(lineChart, 5.0);
		
		Tab tab = new Tab(String.valueOf(tabs.getTabs().size() + 1));
		tab.setContent(anchor);
		tabs.getTabs().add(tab);
		tabs.getSelectionModel().select(tab);
	}
	
	/**
	 * Данные, необходимые для отображения графика.
	 * 
	 * @author Иванов Илья
	 * @since 2013-05-19
	 *
	 */
	public static class GraphicData {
		
		private Function approximateSolution;
		private Function exactSolution;
		private Bounds bounds;
		private int segmentsCount;
		
		/**
		 * @param approximateSolution Приближённое решение задачи.
		 * @param exactSolution Точное решение задачи (если известно).
		 * @param bounds Границы задачи.
		 * @param segmentsCount Количество частичных отрезков, используемых при построении графиков.
		 */
		public GraphicData(Function approximateSolution, Function exactSolution, Bounds bounds, int segmentsCount) {
			if (approximateSolution == null)
				throw new RuntimeException("Не задано приближённое решение для отображения");
			if (bounds == null)
				throw new RuntimeException("Не заданы границы задачи для отображения решения");
			if (segmentsCount < 1)
				throw new RuntimeException("Количество частичных отрезков должно быть положительным");
			
			this.approximateSolution = approximateSolution;
			this.exactSolution = exactSolution;
			this.bounds = bounds;
			this.segmentsCount = segmentsCount;
		}
		
		/**
		 * @return Приближённое решение задачи.
		 */
		public Function getApproximateSolution() {
			return approximateSolution;
		}
		
		/**
		 * @return Точное решение задачи (если известно).
		 */
		public Function getExactSolution() {
			return exactSolution;
		}
		
		/**
		 * @return Границы задачи.
		 */
		public Bounds getBounds() {
			return bounds;
		}
		
		/**
		 * @return Количество частичных отрезков, используемых при построении графиков.
		 */
		public int getSegmentsCount() {
			return segmentsCount;
		}
	}
}